/*
 * Copyright (c) 2023-2024 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.injector;

import static java.util.stream.Collectors.toList;

import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.methods.Delete;
import com.baomidou.mybatisplus.core.injector.methods.DeleteById;
import com.baomidou.mybatisplus.core.injector.methods.DeleteByIds;
import com.baomidou.mybatisplus.core.injector.methods.Insert;
import com.baomidou.mybatisplus.core.injector.methods.SelectById;
import com.baomidou.mybatisplus.core.injector.methods.SelectByIds;
import com.baomidou.mybatisplus.core.injector.methods.SelectCount;
import com.baomidou.mybatisplus.core.injector.methods.SelectMaps;
import com.baomidou.mybatisplus.core.injector.methods.SelectObjs;
import com.baomidou.mybatisplus.core.injector.methods.Update;
import com.baomidou.mybatisplus.core.injector.methods.UpdateById;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.extension.injector.methods.AlwaysUpdateSomeColumnById;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.Configuration;
import org.elsfs.cloud.common.mybatis.ext.mybatis.PredicateHolder;
import org.elsfs.cloud.common.mybatis.injector.methods.DeleteBatchByIdIgnoreLogicDelete;
import org.elsfs.cloud.common.mybatis.injector.methods.DeleteByIdIgnoreLogicDelete;
import org.elsfs.cloud.common.mybatis.injector.methods.ExistsById;
import org.elsfs.cloud.common.mybatis.injector.methods.ExistsByJoinWrapper;
import org.elsfs.cloud.common.mybatis.injector.methods.ExistsByWrapper;
import org.elsfs.cloud.common.mybatis.injector.methods.RestoreBatchById;
import org.elsfs.cloud.common.mybatis.injector.methods.RestoreById;
import org.elsfs.cloud.common.mybatis.injector.methods.SelectJoinCount;
import org.elsfs.cloud.common.mybatis.injector.methods.SelectJoinList;
import org.elsfs.cloud.common.mybatis.injector.methods.SelectJoinOne;
import org.elsfs.cloud.common.mybatis.injector.methods.SelectJoinPage;
import org.elsfs.cloud.common.mybatis.injector.methods.SelectList;
import org.elsfs.cloud.common.mybatis.injector.methods.SelectListIgnoreLogicDelete;
import org.elsfs.cloud.common.mybatis.injector.methods.SelectParentTree;

/**
 * 自定义sql注入器
 *
 * @author zeng
 */
@Slf4j
public class ElsfsSqlInjector extends DefaultSqlInjector {
  public ElsfsSqlInjector() {
    LOGGER.info("elsfs sql injector init");
  }

  @Override
  public List<AbstractMethod> getMethodList(
      Configuration configuration, Class<?> mapperClass, TableInfo tableInfo) {
    GlobalConfig.DbConfig dbConfig = GlobalConfigUtils.getDbConfig(configuration);
    Stream.Builder<AbstractMethod> builder =
        Stream.<AbstractMethod>builder()
            .add(new Insert(dbConfig.isInsertIgnoreAutoIncrementColumn()))
            .add(new Delete())
            .add(new Update())
            .add(new SelectCount())
            .add(new SelectMaps())
            .add(new SelectObjs());
    //      .add(new com.baomidou.mybatisplus.core.injector.methods.SelectList()); // 替换为自定义的
    if (tableInfo.havePK()) {
      builder
          .add(new DeleteById())
          .add(new DeleteByIds())
          .add(new UpdateById())
          .add(new SelectById())
          .add(new SelectByIds());
    } else {
      logger.warn(
          String.format(
              "%s ,Not found @TableId annotation, Cannot use Mybatis-Plus 'xxById' Method.",
              tableInfo.getEntityType()));
    }
    builder.add(new SelectListIgnoreLogicDelete());
    builder.add(new DeleteByIdIgnoreLogicDelete());
    builder.add(new RestoreById());
    builder.add(new RestoreBatchById());
    builder.add(new DeleteBatchByIdIgnoreLogicDelete());
    builder.add(new SelectList()); // 替换为自定义的

    ThreadLocalPredicate threadLocalPredicate = new ThreadLocalPredicate();
    builder.add(new AlwaysUpdateSomeColumnById(threadLocalPredicate));
    builder.add(new InsertBatchSomeColumn(threadLocalPredicate));
    builder.add(new ExistsById());
    builder.add(new ExistsByWrapper());
    builder.add(new ExistsByJoinWrapper());
    builder.add(new SelectJoinCount());
    builder.add(new SelectJoinOne());
    builder.add(new SelectJoinList());
    builder.add(new SelectJoinPage());
    builder.add(new SelectParentTree());

    return builder.build().collect(toList());
  }

  static class ThreadLocalPredicate implements Predicate<TableFieldInfo> {

    /**
     * Evaluates this predicate on the given argument.
     *
     * @param tableFieldInfo the input argument
     * @return {@code true} if the input argument matches the predicate, otherwise {@code false}
     */
    @Override
    public boolean test(TableFieldInfo tableFieldInfo) {
      Predicate<TableFieldInfo> predicate = PredicateHolder.get();
      if (predicate == null) {
        return true;
      }

      return predicate.test(tableFieldInfo);
    }
  }
}
